#include "MemoDialog.h"
#include <QFontDialog>
#include <QFileDialog>
#include <QColorDialog>
#include <QKeyEvent>
#include <QTextDocumentFragment>
#include <QClipBoard>
#include <QMimeData>
#include <QImageReader>
#include <QMenu>
#include "LxMemo.h"
#include "db.h"
//#include "ColorChunk.h"

MemoDialog::MemoDialog(SharedMemo memo, sqlite3* db)
    : QWidget(nullptr), _db(db)
{
    ui.setupUi(this);

    SetMemo(memo);

    ui.textEdit->document()->setIndentWidth(4);
    ui.textEdit->viewport()->installEventFilter(this);
    ui.textEdit->installEventFilter(this);
    ui.textEdit->setUndoRedoEnabled(true);

    connect(ui.textEdit, &QTextEdit::customContextMenuRequested, this, &MemoDialog::onMenuPop);
    connect(ui.textEdit, &QTextEdit::selectionChanged, this, &MemoDialog::onSelectChanged);

    _searcher = new Searcher(ui.textEdit->document(), this);
    _searcher->hide();

    _editTool = new EditTools(ui.textEdit, this);
    //_editTool->setWindowFlag(Qt::ToolTip);

    _editTool->hide();

    connect(_editTool, &EditTools::changeBold, this, &MemoDialog::onTextBold);
    connect(_editTool, &EditTools::changeItalic, this, &MemoDialog::onTextItalic);
    connect(_editTool, &EditTools::changeUnderline, this, &MemoDialog::onTextUnderline);
    connect(_editTool, &EditTools::changeStrikeout, this, &MemoDialog::onTextStrikeout);
    connect(_editTool, &EditTools::changeFont, this, &MemoDialog::onTextFontChanged);
    connect(_editTool, &EditTools::changeFontSize, this, &MemoDialog::onTextFontSize);
    connect(_editTool, &EditTools::changeFontColor, this, &MemoDialog::onTextColorChanged);
    connect(_editTool, &EditTools::changeBackgroundColor, this, &MemoDialog::onTextBkColorChanged);
    connect(_editTool, &EditTools::changeFontUp, this, &MemoDialog::onTextColorUp);
    connect(_editTool, &EditTools::changeFontDown, this, &MemoDialog::onTextColorDown);

    _sizeSelector = new LucencyDialog(this);
    _selector = new SizeSelector(_sizeSelector);
    _sizeSelector->SetWidget(_selector);
    _sizeSelector->SetFixedEnable(false);
    _sizeSelector->SetPureStyle("rgb(235,229,209)");
    _sizeSelector->SetAttribute(~WA_MINIMUM_WINDOW & ~WA_MAXIMUM_WINDOW);
    _sizeSelector->resize(230, 80);
    _sizeSelector->hide();

    connect(_selector, &SizeSelector::SelectFinished, this, &MemoDialog::onImageSelected);

    init();
}

MemoDialog::~MemoDialog()
{}

void MemoDialog::SetMemo(SharedMemo memo)
{
    _memo = memo;

    ui.textEdit->setHtml(_memo->GetHtml());
}

uint32_t MemoDialog::MemoId()
{
    if (!_memo) return 0;

    return _memo->Id();
}

void MemoDialog::onWindowClosed()
{
    _searcher->ResetSearcher();
    _editTool->close();

    _keep.store(false);
    _cv.notify_all();

    _thread->join();
    delete _thread;
}

void MemoDialog::closeEvent(QCloseEvent* e)
{
    onWindowClosed();

    QWidget::closeEvent(e);
}

void MemoDialog::keyPressEvent(QKeyEvent* e)
{
    auto ctrl = e->modifiers();
    auto key = e->key();

    if (Qt::ControlModifier == ctrl)
    {
        if (Qt::Key_S == key && !_saveRight) {
            _saveRight = true;
            _cv.notify_one();
        }
        if (Qt::Key_F == key) {
            onTextSearch(true);
        }
        if (Qt::Key_K == key) {
            auto cursor = ui.textEdit->textCursor();
            auto format = cursor.charFormat();

            if (format.verticalAlignment() != QTextCharFormat::AlignNormal) {
                format.setVerticalAlignment(QTextCharFormat::AlignNormal);
                cursor.insertText(" ");

                QTextCursor tempCursor(cursor);
                tempCursor.setPosition(cursor.selectionStart() - 1);
                tempCursor.setPosition(cursor.selectionEnd(), QTextCursor::KeepAnchor);
                tempCursor.mergeCharFormat(format);
            }
        }
        if (Qt::Key_U == key) {
            auto cursor = ui.textEdit->textCursor();
            auto format = cursor.charFormat();
            bool chk = format.verticalAlignment() == QTextCharFormat::AlignNormal;
            onTextColorUp(chk);
        }
        if (Qt::Key_D == key) {
            auto cursor = ui.textEdit->textCursor();
            auto format = cursor.charFormat();
            bool chk = format.verticalAlignment() == QTextCharFormat::AlignNormal;
            onTextColorDown(chk);
        }
    }

    return QWidget::keyPressEvent(e);
}

bool MemoDialog::eventFilter(QObject* watch, QEvent* event)
{
    switch (event->type())
    {
        //case QEvent::Wheel:
        //{
        //    auto e = (QWheelEvent*)event;
        //    auto ctrl = e->modifiers();
        //    if (Qt::ControlModifier == ctrl) {
        //        auto delta = e->pixelDelta();
        //        if (delta.x() > 0) {
        //            ui.textEdit->zoomIn(2);
        //        }
        //        else {
        //            ui.textEdit->zoomOut(2);
        //        }
        //    }
        //    break;
        //}
    case QEvent::MouseButtonPress:
    {
        _editTool->hide();
        _textSelected = false;
        break;
    }
    case QEvent::MouseButtonRelease:
    {
        auto e = (QMouseEvent*)event;
        if (e->button() == Qt::LeftButton) {
            if (_textSelected) {
                auto cursor = ui.textEdit->textCursor();
                auto imp_cursor = ui.textEdit->cursor();
                auto pos = imp_cursor.pos() - QPoint(-10, 80);
                _editTool->UpdateTool(cursor.charFormat());
                _editTool->move(pos);
                _editTool->show();
            }
            else
                _editTool->hide();
        }
        break;
    }
    case QEvent::MouseButtonDblClick:
    {
        auto cursor = ui.textEdit->textCursor();
        auto fmt = cursor.charFormat();
        switch (fmt.objectType())
        {
            ///FIXME: 查看图片
        case QTextFormat::ImageObject:
        {
            auto prop = fmt.properties();
            for (auto& it : prop) {
                auto k = it;
            }

            break;
        }
        default:break;
        }
        break;
    }
    case QEvent::MouseMove: {
        if (!_editTool->isHidden()) {
            auto rt = _editTool->geometry();
            rt.adjust(-40, -40, 40, 40);

            auto pos = ((QMouseEvent*)event)->pos();
            pos = mapToGlobal(pos);

            if (!rt.contains(pos)) {
                _editTool->hide();
            }
        }
        break;
    }
    default:
        break;
    }

    return QWidget::eventFilter(watch, event);
}

void MemoDialog::resizeEvent(QResizeEvent* event)
{
    if (!_searcher->isHidden()) {
        _searcher->resize(ui.textEdit->width(), 30);
        _searcher->move(-3, height() - 35);
    }
}

void MemoDialog::focusOutEvent(QFocusEvent* event)
{
    QWidget::focusOutEvent(event);
}

void MemoDialog::onTextBold(bool checked)
{
    changeProp([&](QTextCharFormat& format) {format.setFontWeight(checked ? QFont::Bold : QFont::Normal); });
}

void MemoDialog::onTextItalic(bool checked)
{
    changeProp([&](QTextCharFormat& format) {format.setFontItalic(checked); });
}

void MemoDialog::onTextUnderline(bool checked)
{
    changeProp([&](QTextCharFormat& format) {format.setFontUnderline(checked); });
}

void MemoDialog::onTextStrikeout(bool checked)
{
    changeProp([&](QTextCharFormat& format) {format.setFontStrikeOut(checked); });
}

void MemoDialog::onTextFontSize(const QString& s)
{
    if (s.isEmpty())
        return;

    auto font = ui.textEdit->textCursor().charFormat().font();
    font.setPointSize(s.toInt());
    changeProp([&](QTextCharFormat& format) {format.setFont(font); });
}

void MemoDialog::onInsertImage()
{
    /* auto cursor = ui.textEdit->textCursor();*/
    auto imgFile = QFileDialog::getOpenFileName(nullptr, tr("Image"), QString(), tr("JPG (*.jpeg *.jpg);;PNG (*.png);;XMP (*.xpm)"));
    if (!imgFile.isEmpty()) {
        _selector->Init(imgFile);
        _sizeSelector->show();
    }
}

void MemoDialog::onSymbolChecked(bool checked)
{
    //if (checked) {
    auto cursor = ui.textEdit->textCursor();
    auto list = cursor.currentList();

    QTextListFormat listFormat;

    listFormat.setStyle(QTextListFormat::ListDisc);
    listFormat.setIndent(3);

    if (!list) {
        list = cursor.createList(listFormat);
        list->setFormat(listFormat);
    }
    else {
        QTextListFormat listFormat;
        listFormat.setStyle(QTextListFormat::ListCircle);
        listFormat.setIndent(6);
        auto list = cursor.createList(listFormat);
        list->setFormat(listFormat);
    }
}

void MemoDialog::onMenuPop(const QPoint& pos)
{
    QMenu menu;

    auto copyAction = menu.addAction(QIcon(":/LxMemo/icons/copy.png"), tr("Copy"));
    auto cutAction = menu.addAction(QIcon(":/LxMemo/icons/cut.png"), tr("Cut"));
    auto textPasteAction = menu.addAction(QIcon(":/LxMemo/icons/paste.png"), tr("Paste"));
    menu.addSeparator();

    auto findAction = menu.addAction(QIcon(":/LxMemo/icons/search.png"), tr("&Find"));
    auto insertImgAction = menu.addAction(QIcon(":/LxMemo/icons/picture.png"), tr("Insert"));
    auto addSymbolAction = menu.addAction(QIcon(":/LxMemo/icons/symbol.png"), tr("Bullets"));

    auto colorAction = menu.addAction(QIcon(":/LxMemo/icons/highlight.png"), tr("Highlight"));
    auto clearAction = menu.addAction(QIcon(":/LxMemo/icons/clear_format.png"), tr("Clear Format"));
    //auto addUrlAction = menu.addAction(QIcon(""), "Add URL");
    menu.addSeparator();
    auto selAllAction = menu.addAction(QIcon(":/LxMemo/icons/selall.png"), tr("Select All"));

    auto cursor = ui.textEdit->textCursor();
    if (!cursor.hasSelection())
        copyAction->setEnabled(false);

    std::function<void(bool)> selfn = [this](bool)
    {
        ui.textEdit->selectAll();
    };

    std::function<void(bool)> clrfn = [this](bool)
    {
        changeProp([&](QTextCharFormat& format) {
            auto background = format.background();
            auto fore = format.foreground();
            if (background.color() != Qt::darkGreen)
                format.setBackground(Qt::darkGreen);
            else
                format.setBackground(Qt::transparent);
            format.setForeground(fore);
            });
    };

    std::function<void(bool)> clearfn = [this](bool)
    {
        auto cursor = ui.textEdit->textCursor();
        auto bfmt = cursor.blockFormat();
        bfmt.setBackground(Qt::transparent);
        cursor.mergeBlockFormat(bfmt);

        auto cfmt = cursor.charFormat();
        cfmt.setBackground(Qt::transparent);
        cursor.mergeCharFormat(cfmt);
    };

    std::function<void(bool)> cutfn = [this](bool)
    {
        auto clip = qApp->clipboard();
        auto cursor = ui.textEdit->textCursor();
        auto text = cursor.selection();

        QMimeData* mime = new QMimeData();
        mime->setHtml(text.toHtml());
        clip->setMimeData(mime);
        cursor.removeSelectedText();
    };

    std::function<void(bool)> copyfn = [this](bool)
    {
        auto cursor = ui.textEdit->textCursor();
        auto text = cursor.selection();

        QMimeData* mime = new QMimeData();
        mime->setHtml(text.toHtml());
        qApp->clipboard()->setMimeData(mime);
    };

    std::function<void(bool)> pastetextfn = [this](bool)
    {
        auto cursor = ui.textEdit->textCursor();
        auto clip = qApp->clipboard();
        auto mime = clip->mimeData();
        if (mime->hasText())
            cursor.insertText(mime->text(), QTextCharFormat());
        else if (mime->hasHtml())
            cursor.insertHtml(mime->html());
        else if (mime->hasImage()) {
            auto imgid = GenId();
            auto imgname = tr(".cache/%1/%2.png").arg(QString::number(_memo->Id()), QString::number(imgid));
            auto img = qvariant_cast<QImage>(mime->imageData());
            img.save(imgname);
            //QTextImageFormat fmt;
            //fmt.setWidth(img.width());
            //fmt.setHeight(img.height());
            //fmt.setName(imgname);
            //cursor.insertImage(fmt);

            _selector->Init(imgname);
            _sizeSelector->show();
        }
    };

    /*
    std::function<void(bool)> addurlfn = [this](bool)
    {
        //const char* url = "https://www.baidu.com";
        const char* url = R"(<a href="url">https://www.baidu.com</a>)";
        auto cursor = ui.textEdit->textCursor();
        cursor.insertHtml(url);
    };
    */
    connect(colorAction, &QAction::triggered, this, clrfn);
    connect(clearAction, &QAction::triggered, this, clearfn);
    connect(selAllAction, &QAction::triggered, this, selfn);
    connect(copyAction, &QAction::triggered, this, copyfn);
    connect(cutAction, &QAction::triggered, this, cutfn);
    connect(textPasteAction, &QAction::triggered, this, pastetextfn);

    connect(insertImgAction, &QAction::triggered, this, &MemoDialog::onInsertImage);
    connect(findAction, &QAction::triggered, this, &MemoDialog::onTextSearch);
    connect(addSymbolAction, &QAction::triggered, this, &MemoDialog::onSymbolChecked);
    //connect(addUrlAction, &QAction::triggered, this, addurlfn);

    auto npos = mapToGlobal(pos);
    menu.exec(npos);
}

void MemoDialog::onSelectChanged()
{
    auto cursor = ui.textEdit->textCursor();
    if (cursor.hasSelection())
        _textSelected = true;
}

void MemoDialog::onTextSearch(bool check)
{
    //if (!check)
    //    _searcher->hide();
    //else {
    _searcher->resize(ui.textEdit->width(), 30);
    _searcher->move(-3, height() - 35);
    _searcher->show();
    _searcher->setFocus();
    //}
}

void MemoDialog::onImageSelected(const QTextImageFormat& format)
{
    _sizeSelector->hide();
    //_selector->hide();
    //此处可插入原图，当前对图片进行了缩放处理
    auto cursor = ui.textEdit->textCursor();
    cursor.insertImage(format, QTextFrameFormat::InFlow);
}

void MemoDialog::onTextFontChanged(const QFont& font)
{
    changeProp([&](QTextCharFormat& format) {format.setFont(font); });
}

void MemoDialog::onTextColorChanged(const QColor& color)
{
    changeProp([&](QTextCharFormat& format) {format.setForeground(color); });
}

void MemoDialog::onTextBkColorChanged(const QColor& color)
{
    changeProp([&](QTextCharFormat& format) {format.setBackground(color); });
}

void MemoDialog::onTextColorUp(bool checked)
{
    changeProp([&](QTextCharFormat& format) {
        auto align = checked ? QTextCharFormat::AlignSuperScript : QTextCharFormat::AlignNormal;
        format.setVerticalAlignment(align);
        });
}

void MemoDialog::onTextColorDown(bool checked)
{
    changeProp([&](QTextCharFormat& format) {
        auto align = checked ? QTextCharFormat::AlignSubScript : QTextCharFormat::AlignNormal;
        format.setVerticalAlignment(align);
        });
}

void MemoDialog::init()
{
    _thread = new std::thread(std::bind(&MemoDialog::saveInThread, this));
}

void MemoDialog::changeProp(auto cb)
{
    ///Q:从左往右和从右往左选中文本，产生的效果不同
    ///我也很纳闷，是QTextEdit自身的问题还是实现的问题

    auto cursor = ui.textEdit->textCursor();
    auto start = cursor.selectionStart();
    auto end = cursor.selectionEnd();
    auto doc = ui.textEdit->document();
    auto firstBlock = doc->findBlock(start);
    auto lastBlock = doc->findBlock(end);
    int i = firstBlock.blockNumber();
    int j = lastBlock.blockNumber();

    ///将所有的编辑当做一个事务
    //std::map<std::tuple<int, int, QTextBlock*>, QTextFragment> modify;
    cursor.beginEditBlock();
    for (j; j >= i; j--)
    {
        auto block = doc->findBlockByNumber(j);
        if (!block.isValid())
            continue;

        auto it = block.begin();
        std::map<std::pair<int, int>, QTextFragment> modify;
        for (it; it != block.end(); ++it) {
            auto frag = it.fragment();
            int from = frag.position();
            int to = frag.position() + frag.length();

            if (to < start) continue;
            if (from > end) break;

            int s = start, e = end;
            if (from > s) s = from;
            if (to < e) e = to;

            modify.emplace(std::make_pair(s, e), frag);
        }
        QTextCursor tempCursor(block);
        for (auto& [k, frag] : modify) {
            auto format = frag.charFormat();
            cb(format);

            tempCursor.setPosition(k.first);
            tempCursor.setPosition(k.second, QTextCursor::KeepAnchor);
            tempCursor.mergeCharFormat(format);
        }
    }

    cursor.endEditBlock();
}

void MemoDialog::saveInThread()
{
    do
    {
        ///每4s中检测一次文档是否被修改
        std::unique_lock<std::mutex> lck(_mtx);
        _cv.wait_for(lck, std::chrono::seconds(4), [&] {return !_keep || _saveRight; });

        save();
    } while (_keep);
}

void MemoDialog::save()
{
    auto doc = ui.textEdit->document();
    if (doc->isModified()) {
        doc->setModified(false);

        auto html = doc->toHtml();
        auto content = html.toLocal8Bit().toBase64();
        int len = content.length();

        _memo->SetHtml(html);
        auto prop = _memo->PropertyToStream();
        auto snap = _memo->Snapshot().toLocal8Bit().toBase64();
        auto time = QDateTime::currentDateTime().toString();
        _memo->SetTime(time);

        std::unique_ptr<char> data(new char[len + prop.length() + snap.length() + 256]{ 0 });
        int r = sprintf(data.get(), R"(update Memo set DATA="%s", PROP="%s", SNAPSHOT="%s", TIME="%s" where ID=%u;)",
            content.data(),
            prop.data(),
            snap.data(),
            time.toLocal8Bit().data(),
            _memo->Id());

        ExecSQL(_db, data.get());

        emit MemoUpdate(_memo->Id());
    }

    _saveRight = false;
}